#pragma once
#include <cassert>

#include "Market.h"


class Dealing;
class Trade
{
public:
	Trade(Dealing& dealing, double dealPrice, int dealQuantity,char const* systemNo)
		:m_dealing(&dealing),m_dealPrice(dealPrice),m_dealQuantity(dealQuantity)
	{
		strcpy_s(m_systemNo,systemNo);
	}
	int GetContract() const;
	Dealing const& GetDealing() const { return *m_dealing; }
	Dealing& GetDealing() { return *m_dealing; }
	double GetDealPrice() const { return m_dealPrice; }
	int GetDealQuantity() const { return m_dealQuantity; }

	double GetTotal() const {return m_dealPrice * m_dealQuantity;}
private:
	Dealing* m_dealing;
	double m_dealPrice;
	int m_dealQuantity;
	char m_systemNo[50];
};

class Business;

struct Order
{
	Business* businessPointer;
	int businessId;
	char contractCode[20];
	int contract;
	Direction::TYPE direction;
	Operation::TYPE operation;
	int localId;
	double price;
	int quantity;
};

class Dealing
{
public:
	enum STATUS {SEND,ACCEPT,SEND_FAILED,ORDERED,PARTIAL,COMPLETE,CANCEL_SEND_FAILED,CANCELED,WAIT_TRADE};
	typedef int KeyType;
	int GetKey() const {return m_orderInfo.localId;}

	explicit Dealing(Customer& customer,Order const& orderInfo)
	:m_customer(&customer),
	m_orderInfo(orderInfo),
	m_status(SEND),
	m_averageDealPrice(0),
	m_dealQuantity(0),
	m_remainQuantity(orderInfo.quantity),
	m_cancelQuantity(0)
	{
		memset(m_systemNo,0,sizeof(m_systemNo));
	}

	bool IsBuy() const
	{
		return GetDirection() == Direction::BUY;
	}

	bool IsSell() const
	{
		return GetDirection() == Direction::SELL;
	}

	bool IsOpenOrder() const
	{
		return GetOperation() == Operation::OPEN;
	}
	
	bool IsOffsetOrder() const
	{
		return !IsOpenOrder();
	}


	bool OnSendFailed()
	{
		if (IsSend() || IsSendFailed())
		{
			m_status = SEND_FAILED;
			return true;
		}
		return false;
	}

	bool OnAccept()
	{
		if (IsSend())
		{
			m_status = ACCEPT;
			return true;
		}
		return false;
	}

	bool OnOrdered(char const* systemNo)
	{
		assert(systemNo != 0);

		if (IsSend() || IsAccept() || IsSendFailed())
		{
			strcpy_s(m_systemNo,systemNo);
			m_status = ORDERED;
			return true;
		}
		else
		{
			return false;
		}
	}

	bool OnTrade(Trade const& trade)
	{
		if (IsOrdered() || IsPartial())
		{
			int remainQuantity = m_orderInfo.quantity - m_dealQuantity - trade.GetDealQuantity();
			if (remainQuantity >= 0)
			{
				m_dealQuantity += trade.GetDealQuantity();
				m_remainQuantity = m_orderInfo.quantity - m_dealQuantity;
				m_trades.push_back(trade);
				m_averageDealPrice = CalcAverageDealPrice();

				m_status = m_remainQuantity == 0 ? COMPLETE : PARTIAL;
				return true;
			}
			else
			{
				//some big error
			}
		}
		if (IsWaitTrade())
		{
			int dealQuantity = m_orderInfo.quantity - m_cancelQuantity;
			if (dealQuantity == m_dealQuantity + trade.GetDealQuantity())
			{
				m_status = CANCELED;
				m_dealQuantity = dealQuantity;
				m_remainQuantity = 0;
				m_trades.push_back(trade);
				m_averageDealPrice = CalcAverageDealPrice();
				return true;
			}
			else
			{
				//some big error
			}

		}
		return false;
	}
	bool OnCancelSendFailed()
	{
		if (IsOrdered() || IsPartial() || IsCancelSendFailed())
		{
			m_status = CANCEL_SEND_FAILED;
			return true;
		}
		return false;
	}

	bool OnCancelSuccess(int dealQuantity)
	{
		if (IsOrdered() || IsPartial() || IsCancelSendFailed())
		{
			if (m_dealQuantity < dealQuantity)
				m_status = WAIT_TRADE;
			else
				m_status = CANCELED;
			m_remainQuantity = 0;
			m_cancelQuantity = m_orderInfo.quantity - dealQuantity;
			return true;
		}
		return false;
	}

	char const* GetStatusName() const
	{
		switch (m_status)
		{
		case SEND:
			return "send";
		case SEND_FAILED:
			return "sent failed";
		case ACCEPT:
			return "accepted";
		case ORDERED:
			return "ordered";
		case PARTIAL:
			return "partial";
		case COMPLETE:
			return "complete";
		case CANCEL_SEND_FAILED:
			return "cancel send failed";
		case CANCELED:
			return "canceled";
		default:
			return "unknown";
		}
	}

	bool IsSend() const {return m_status == SEND;}
	bool IsSendFailed() const {return m_status == SEND_FAILED;}
	bool IsAccept() const {return m_status == ACCEPT;}
	bool IsOrdered() const {return m_status == ORDERED;}
	bool IsPartial() const {return m_status == PARTIAL;}
	bool IsComplete() const {return m_status == COMPLETE;}
	bool IsCancelSendFailed() const {return m_status == CANCEL_SEND_FAILED;}
	bool IsCanceled() const {return m_status == CANCELED;}
	bool IsWaitTrade() const {return m_status == WAIT_TRADE;}

	bool IsDealed() const {return IsPartial() ||IsComplete();}
	bool IsFinished() const { return IsComplete() || IsCanceled(); }
	bool IsTradeAndFinished() const {return m_dealQuantity > 0 && (IsCanceled() || IsComplete()) ;}
	bool IsAllCancelled() const { return m_dealQuantity == 0 && IsCanceled(); }

	bool IsSameContract(int contractIndex) const {return m_orderInfo.contract == contractIndex;}
	bool IsSameStatus(Dealing const& other) const {return other.m_status == m_status;}

	Customer& GetCustomer() const { return *m_customer; }
// 	Order const& GetOrderInfo() {return m_orderInfo;}

	int GetContract() const {return m_orderInfo.contract;}
	char const* GetContractCode() const {return m_orderInfo.contractCode;}
	int GetLocalId() const {return m_orderInfo.localId;}
	int GetBusinessId() const {return m_orderInfo.businessId;}
	Business* GetBusiness() const {return m_orderInfo.businessPointer;}
	double GetOrderPrice() const{return m_orderInfo.price;}
	int GetOrderQuantity() const {return m_orderInfo.quantity;}
	int GetDealQuantity() const {return m_dealQuantity;}
	int GetRemainQuantity() const {return m_remainQuantity;}
	int GetCancelQuantity() const {return m_cancelQuantity;}
	Operation::TYPE GetOperation() const {return m_orderInfo.operation;}
	Direction::TYPE GetDirection() const {return m_orderInfo.direction;}

	char const* GetSystemNo() const {return m_systemNo;}
	double GetAverageDealPrice() const {return m_averageDealPrice;}

	Dealing::STATUS GetStatus() const {return m_status;}
// 
// 	void SetLocalId(UINT localId) {m_orderInfo.localId = localId;}
// 	void SetOrderQuantity(UINT orderQuantity) {m_orderInfo.quantity = orderQuantity;}
// 
// 	double GetDealedTotal() const { return GetAverageDealPrice()*GetDealQuantity();}
private:

	double CalcAverageDealPrice()
	{
		double sum = 0.00;
		for (size_t index = 0; index < m_trades.size(); ++index)
		{
			Trade const& trade = m_trades[index];
			sum += trade.GetTotal();
		}
		return sum/m_dealQuantity;
	}

	mutable Customer* m_customer;
	Order m_orderInfo;
	char m_systemNo[50];
	Dealing::STATUS m_status;
	double m_averageDealPrice;
	int m_dealQuantity;
	int m_remainQuantity;
	int m_cancelQuantity;

	std::vector<Trade> m_trades;
};

inline
int Trade::GetContract() const 
{
	return m_dealing->GetContract();
}